@hachej/boring-ui-cli 0.1.13 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -16
- package/dist/index.js +9 -137
- package/dist/server/cli.js +305 -0
- package/dist/server/localWorkspaces.js +159 -0
- package/package.json +19 -5
- package/public/assets/{CodeEditor-DQqOn4xz-43ZRJnBk.js → CodeEditor-DQqOn4xz-KzhiCz--.js} +1 -1
- package/public/assets/{FileTree-BVfqs3rR-Cj1lLRuM.js → FileTree-Dl-qUAB0-B2la_qys.js} +10 -10
- package/public/assets/{MarkdownEditor-CcCDF65H-wNyV9k_h.js → MarkdownEditor-yc6mFsnI-But_i4l5.js} +59 -59
- package/public/assets/{_baseUniq-C6fgNBwH.js → _baseUniq-DdgAi7yq.js} +1 -1
- package/public/assets/{arc-DHH-gLyV.js → arc-CCqUtds5.js} +1 -1
- package/public/assets/{architectureDiagram-Q4EWVU46-kET4ZFwD.js → architectureDiagram-Q4EWVU46-B_KUn6cs.js} +1 -1
- package/public/assets/{blockDiagram-DXYQGD6D-BuyaW2GC.js → blockDiagram-DXYQGD6D-DZgTILG7.js} +1 -1
- package/public/assets/{c4Diagram-AHTNJAMY-DUSmbamx.js → c4Diagram-AHTNJAMY-CJflQswY.js} +1 -1
- package/public/assets/channel-DGvkobtu.js +1 -0
- package/public/assets/{chunk-4BX2VUAB-hMkrRbra.js → chunk-4BX2VUAB-iDI_gnLq.js} +1 -1
- package/public/assets/{chunk-4TB4RGXK-DAkJYV0r.js → chunk-4TB4RGXK-DKWIFYxg.js} +1 -1
- package/public/assets/{chunk-55IACEB6-CQ4cPkeu.js → chunk-55IACEB6-D__oMXZt.js} +1 -1
- package/public/assets/{chunk-EDXVE4YY-BhU6gznk.js → chunk-EDXVE4YY-Do1Ka_m9.js} +1 -1
- package/public/assets/{chunk-FMBD7UC4-CXkmSrcX.js → chunk-FMBD7UC4-lANxhjIk.js} +1 -1
- package/public/assets/{chunk-OYMX7WX6-B6YmKjoP.js → chunk-OYMX7WX6-D5V4Ykah.js} +1 -1
- package/public/assets/{chunk-QZHKN3VN-DaBewk7d.js → chunk-QZHKN3VN-Qd0rJnNE.js} +1 -1
- package/public/assets/{chunk-YZCP3GAM-wVeKMZui.js → chunk-YZCP3GAM-CFwEplp0.js} +1 -1
- package/public/assets/classDiagram-6PBFFD2Q-CbsNjbES.js +1 -0
- package/public/assets/classDiagram-v2-HSJHXN6E-CbsNjbES.js +1 -0
- package/public/assets/clone-CGN5Up_u.js +1 -0
- package/public/assets/{cose-bilkent-S5V4N54A-DI8T3pQ5.js → cose-bilkent-S5V4N54A-DfVWRV6i.js} +1 -1
- package/public/assets/{dagre-KV5264BT-BzhUQX3L.js → dagre-KV5264BT-DwkYud_A.js} +1 -1
- package/public/assets/{diagram-5BDNPKRD-BmjVDoqS.js → diagram-5BDNPKRD-BVmqnZKF.js} +1 -1
- package/public/assets/{diagram-G4DWMVQ6-0qBOUHAQ.js → diagram-G4DWMVQ6-Be8h-c7h.js} +1 -1
- package/public/assets/{diagram-MMDJMWI5-ZuUygJfX.js → diagram-MMDJMWI5-CeQIY42x.js} +1 -1
- package/public/assets/{diagram-TYMM5635-DLicNSlG.js → diagram-TYMM5635-2m1E87ZC.js} +1 -1
- package/public/assets/{erDiagram-SMLLAGMA-BIqDaKsJ.js → erDiagram-SMLLAGMA-CEAPDf64.js} +1 -1
- package/public/assets/{flowDiagram-DWJPFMVM-BuMuUYvo.js → flowDiagram-DWJPFMVM-70-QVqzZ.js} +1 -1
- package/public/assets/{ganttDiagram-T4ZO3ILL-h_0R8kpx.js → ganttDiagram-T4ZO3ILL-CQUeK6YZ.js} +1 -1
- package/public/assets/{gitGraphDiagram-UUTBAWPF-CFZuKUPu.js → gitGraphDiagram-UUTBAWPF-BAxMyzkG.js} +1 -1
- package/public/assets/{graph-BvcPX0LE.js → graph-DBYJAYcR.js} +1 -1
- package/public/assets/{highlighted-body-OFNGDK62-B5LPCsvB.js → highlighted-body-OFNGDK62-D32MCa1A.js} +1 -1
- package/public/assets/index-CyEGNm7Z.css +1 -0
- package/public/assets/index-DfV9nLZO.js +1324 -0
- package/public/assets/{index-CZBRICYr.js → index-Dnr-P_qO.js} +1 -1
- package/public/assets/{infoDiagram-42DDH7IO-C0QkIqtm.js → infoDiagram-42DDH7IO-D4XwdS2Z.js} +1 -1
- package/public/assets/{ishikawaDiagram-UXIWVN3A-DkzZIthw.js → ishikawaDiagram-UXIWVN3A-7F_PxA8e.js} +1 -1
- package/public/assets/{journeyDiagram-VCZTEJTY-BEpMOI6B.js → journeyDiagram-VCZTEJTY-CBJ4F9tD.js} +1 -1
- package/public/assets/{kanban-definition-6JOO6SKY-DGBlgtiV.js → kanban-definition-6JOO6SKY-DNxo2cA_.js} +1 -1
- package/public/assets/{layout-m9mU-Es4.js → layout-xHv827iK.js} +1 -1
- package/public/assets/{linear-gvQOMf93.js → linear-C4D-wE9b.js} +1 -1
- package/public/assets/{min-DbPrSRXJ.js → min-DRKJR04f.js} +1 -1
- package/public/assets/{mindmap-definition-QFDTVHPH-D-w8aNmQ.js → mindmap-definition-QFDTVHPH-BFEYUDaK.js} +1 -1
- package/public/assets/{pieDiagram-DEJITSTG-BJfiij1X.js → pieDiagram-DEJITSTG-OgFPC-fQ.js} +1 -1
- package/public/assets/{quadrantDiagram-34T5L4WZ-SEDnxGzO.js → quadrantDiagram-34T5L4WZ-gcFm5ymy.js} +1 -1
- package/public/assets/{requirementDiagram-MS252O5E-CRXbp7i_.js → requirementDiagram-MS252O5E-C-NXT7Xx.js} +1 -1
- package/public/assets/{sankeyDiagram-XADWPNL6-BQ5yTiY0.js → sankeyDiagram-XADWPNL6-BVTpXbQ9.js} +1 -1
- package/public/assets/{sequenceDiagram-FGHM5R23-D3fAknEI.js → sequenceDiagram-FGHM5R23-6nnB6xSY.js} +1 -1
- package/public/assets/{stateDiagram-FHFEXIEX-DME_bKgM.js → stateDiagram-FHFEXIEX-vvjjWHqR.js} +1 -1
- package/public/assets/stateDiagram-v2-QKLJ7IA2-UKC2Fz8g.js +1 -0
- package/public/assets/{timeline-definition-GMOUNBTQ-CGOu_F4b.js → timeline-definition-GMOUNBTQ-DfgoENQV.js} +1 -1
- package/public/assets/{vennDiagram-DHZGUBPP-BLPdNseJ.js → vennDiagram-DHZGUBPP-CwtXgny7.js} +1 -1
- package/public/assets/{wardley-RL74JXVD-B8d_2bb8.js → wardley-RL74JXVD-YWQ--wCx.js} +1 -1
- package/public/assets/{wardleyDiagram-NUSXRM2D-waI-JAEe.js → wardleyDiagram-NUSXRM2D-0iSD2F3A.js} +1 -1
- package/public/assets/{xychartDiagram-5P7HB3ND-BUIUqkqr.js → xychartDiagram-5P7HB3ND-DpVtsfdW.js} +1 -1
- package/public/index.html +3 -3
- package/public/assets/channel-CUyWgwUc.js +0 -1
- package/public/assets/classDiagram-6PBFFD2Q--KL4rJah.js +0 -1
- package/public/assets/classDiagram-v2-HSJHXN6E--KL4rJah.js +0 -1
- package/public/assets/clone-DAST79i1.js +0 -1
- package/public/assets/index-DKanL3AM.css +0 -1
- package/public/assets/index-y7tKhGxl.js +0 -1349
- package/public/assets/stateDiagram-v2-QKLJ7IA2-Cup0egSL.js +0 -1
package/README.md
CHANGED
|
@@ -1,42 +1,235 @@
|
|
|
1
|
-
# @hachej/boring-ui
|
|
1
|
+
# @hachej/boring-ui-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.npmjs.com/package/@hachej/boring-ui-cli)
|
|
7
|
+
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
**Turn an agent into an app — in one command.** Start a full IDE-style workbench pointed at your current directory: chat, file tree, editor, command palette. No clone. No database. No config.
|
|
6
11
|
|
|
7
12
|
```bash
|
|
8
13
|
npx @hachej/boring-ui-cli
|
|
9
14
|
```
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## TL;DR
|
|
19
|
+
|
|
20
|
+
**The Problem**: You want a coding agent in a browser IDE — but you don't want to clone a repo, set up Postgres, configure auth, or deploy anything. You just want to talk to an AI about your code.
|
|
21
|
+
|
|
22
|
+
**The Solution**: `npx @hachej/boring-ui-cli` starts a full workbench locally, using your current directory as the workspace. It opens a browser tab with chat, file explorer, and panels. Zero setup, zero config, zero deploy.
|
|
23
|
+
|
|
24
|
+
### Why Use @hachej/boring-ui-cli?
|
|
25
|
+
|
|
26
|
+
| Feature | What It Does |
|
|
27
|
+
|---------|--------------|
|
|
28
|
+
| **Zero-config startup** | `npx @hachej/boring-ui-cli` — that's it. Opens your browser to a full agent workbench. |
|
|
29
|
+
| **Simple auth** | Set `ANTHROPIC_API_KEY` in your environment. The agent runs with direct filesystem access to your cwd. |
|
|
30
|
+
| **Full workspace** | Chat, file tree, editor panels, command palette — all running against your real directory. |
|
|
31
|
+
| **No database** | Runs in-memory. State persists for the session. No external dependencies. |
|
|
32
|
+
| **Customizable port + root** | `PORT=8080` and `BORING_AGENT_WORKSPACE_ROOT=/path` env vars for power users. |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Quick Example
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Navigate to any project
|
|
40
|
+
cd /path/to/my-project
|
|
41
|
+
|
|
42
|
+
# Start the CLI — opens browser at localhost:5200
|
|
43
|
+
npx @hachej/boring-ui-cli
|
|
44
|
+
|
|
45
|
+
# With a custom port
|
|
46
|
+
PORT=8080 npx @hachej/boring-ui-cli
|
|
47
|
+
|
|
48
|
+
# Point at a specific directory
|
|
49
|
+
BORING_AGENT_WORKSPACE_ROOT=/path/to/project npx @hachej/boring-ui-cli
|
|
50
|
+
|
|
51
|
+
# With API key
|
|
52
|
+
ANTHROPIC_API_KEY=sk-ant-... npx @hachej/boring-ui-cli
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Once the browser opens, you can:
|
|
56
|
+
```
|
|
57
|
+
# In the chat box:
|
|
58
|
+
"read my README and suggest improvements"
|
|
59
|
+
"find all TypeScript files that import 'react'"
|
|
60
|
+
"rewrite the test file to use vitest"
|
|
61
|
+
```
|
|
12
62
|
|
|
13
63
|
---
|
|
14
64
|
|
|
15
|
-
##
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
No installation needed — use `npx`:
|
|
16
68
|
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
69
|
+
```bash
|
|
70
|
+
npx @hachej/boring-ui-cli
|
|
71
|
+
```
|
|
20
72
|
|
|
21
|
-
|
|
73
|
+
Or install globally for repeated use:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# npm
|
|
77
|
+
npm install -g @hachej/boring-ui-cli
|
|
78
|
+
|
|
79
|
+
# pnpm
|
|
80
|
+
pnpm add -g @hachej/boring-ui-cli
|
|
81
|
+
|
|
82
|
+
# Then just run:
|
|
83
|
+
boring-ui
|
|
84
|
+
```
|
|
22
85
|
|
|
23
|
-
|
|
86
|
+
### From Source
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/hachej/boring-ui.git
|
|
90
|
+
cd boring-ui && pnpm install
|
|
91
|
+
pnpm --filter @hachej/boring-ui-cli build
|
|
92
|
+
npx ./packages/cli/dist/index.js
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Authentication
|
|
98
|
+
|
|
99
|
+
The CLI talks to Anthropic Claude via the agent runtime. You need a valid API key:
|
|
24
100
|
|
|
25
101
|
```bash
|
|
26
102
|
ANTHROPIC_API_KEY=sk-ant-... npx @hachej/boring-ui-cli
|
|
27
103
|
```
|
|
28
104
|
|
|
29
|
-
|
|
105
|
+
Only Anthropic Claude is wired in v1. The agent harness supports other providers via the `AgentHarness` interface, but only Anthropic is implemented.
|
|
106
|
+
|
|
107
|
+
---
|
|
30
108
|
|
|
31
109
|
## Options
|
|
32
110
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
111
|
+
| Environment Variable | Default | Description |
|
|
112
|
+
|---------------------|---------|-------------|
|
|
113
|
+
| `ANTHROPIC_API_KEY` | Yes | Anthropic API key. The agent requires a valid key to function. |
|
|
114
|
+
| `PORT` | `5200` | Port to run the server on |
|
|
115
|
+
| `BORING_AGENT_WORKSPACE_ROOT` | `.` (cwd) | Root directory for the workspace. The agent sees this as its filesystem. |
|
|
116
|
+
| `BORING_AGENT_MODE` | `direct` | `direct` (no sandbox) or `local` (bwrap sandbox, Linux only) |
|
|
117
|
+
| `BORING_AGENT_DEFAULT_MODEL_ID` | `claude-sonnet-4-6` | Default model to use |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Architecture
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
npx @hachej/boring-ui-cli
|
|
125
|
+
├── Boot Fastify server (direct mode, in-memory)
|
|
126
|
+
├── Serve frontend SPA (Vite-built bundle)
|
|
127
|
+
├── Open browser → http://localhost:5200
|
|
128
|
+
└── Workspace = your current directory (or $BORING_AGENT_WORKSPACE_ROOT)
|
|
36
129
|
```
|
|
37
130
|
|
|
131
|
+
The CLI is the zero-config entry point to the full boring-ui stack. Under the hood it wires together:
|
|
132
|
+
|
|
133
|
+
- `@hachej/boring-agent` — agent runtime, tools, chat UI
|
|
134
|
+
- `@hachej/boring-workspace` — file tree, panels, command palette, plugins
|
|
135
|
+
- `@hachej/boring-ui-kit` — shared UI primitives
|
|
136
|
+
|
|
137
|
+
All running locally against your real filesystem with no database.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## How @hachej/boring-ui-cli Compares
|
|
142
|
+
|
|
143
|
+
| Feature | @hachej/boring-ui-cli | Claude Code | Codex CLI | Cursor |
|
|
144
|
+
|---------|------------------------|-------------|-----------|--------|
|
|
145
|
+
| Browser UI | ✅ Full IDE with panels | ❌ Terminal only | ❌ Terminal only | ✅ Desktop app |
|
|
146
|
+
| File tree | ✅ Side panel | ❌ | ❌ | ✅ |
|
|
147
|
+
| Zero setup | ✅ `npx` anywhere | ⚠️ Install + login | ⚠️ Install + login | ❌ Desktop app download |
|
|
148
|
+
| Panel system | ✅ Dockview splittable panels | ❌ | ❌ | ❌ |
|
|
149
|
+
| Plugin extensibility | ✅ Panels, commands, catalogs | ❌ | ❌ | ⚠️ Extensions |
|
|
150
|
+
| Local filesystem | ✅ Direct access | ✅ | ✅ | ✅ |
|
|
151
|
+
| Database required | ❌ None | ❌ | ❌ | ❌ |
|
|
152
|
+
|
|
153
|
+
**When to use @hachej/boring-ui-cli:**
|
|
154
|
+
- You want a browser-based coding agent with file tree and panels
|
|
155
|
+
- You don't want to install anything — just `npx`
|
|
156
|
+
- You want plugin extensibility (custom panels, data catalogs, etc.)
|
|
157
|
+
|
|
158
|
+
**When it might not fit:**
|
|
159
|
+
- You prefer terminal-only agent workflows (use Claude Code or Codex CLI)
|
|
160
|
+
- You need multi-user auth, workspaces, or a database (use `@hachej/boring-core`)
|
|
161
|
+
- You want a full desktop IDE with LSP, debugging, and git (use Cursor or VS Code)
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Troubleshooting
|
|
166
|
+
|
|
167
|
+
| Error | Cause | Fix |
|
|
168
|
+
|-------|-------|-----|
|
|
169
|
+
| `ANTHROPIC_API_KEY not set` | No API key | `export ANTHROPIC_API_KEY=sk-ant-...` before running |
|
|
170
|
+
| `port already in use` | Port 5200 occupied | `PORT=5201 npx @hachej/boring-ui-cli` |
|
|
171
|
+
| Browser doesn't open | `BROWSER=none` or no display | Manually navigate to `http://localhost:5200` |
|
|
172
|
+
| Agent returns errors | Invalid API key | Verify your Anthropic API key is valid and has quota |
|
|
173
|
+
| `workspace root not found` | `BORING_AGENT_WORKSPACE_ROOT` points to non-existent dir | Create the directory or unset the variable to use cwd |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Limitations
|
|
178
|
+
|
|
179
|
+
- **In-memory only**: No database, no persistent workspaces. State is lost when the CLI exits.
|
|
180
|
+
- **Single workspace**: Points at one directory. No multi-workspace switching.
|
|
181
|
+
- **No auth management**: No user accounts, invites, or role-based access.
|
|
182
|
+
- **Direct mode only**: No bwrap sandbox by default (use `BORING_AGENT_MODE=local` on Linux with bubblewrap installed).
|
|
183
|
+
- **Not for production**: This is a developer tool, not a deployment target. Use `@hachej/boring-core` for multi-user apps.
|
|
184
|
+
- **Only Anthropic Claude**: No OpenAI, Google, or other model providers wired in v1.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## FAQ
|
|
189
|
+
|
|
190
|
+
**Q: Do I need to install anything first?**
|
|
191
|
+
A: No. `npx` downloads and runs the package on first use. Subsequent runs use the cached version.
|
|
192
|
+
|
|
193
|
+
**Q: What happens when I close the browser?**
|
|
194
|
+
A: The server keeps running. Stop it with `Ctrl+C` in the terminal.
|
|
195
|
+
|
|
196
|
+
**Q: Can I use this with OpenAI models?**
|
|
197
|
+
A: Only Anthropic Claude is wired in v1. Additional providers may be supported in future versions.
|
|
198
|
+
|
|
199
|
+
**Q: Is my code sent to the cloud?**
|
|
200
|
+
A: Yes — the agent sends file contents and chat messages to the LLM provider (e.g. Anthropic). The filesystem operations run locally on your machine.
|
|
201
|
+
|
|
202
|
+
**Q: How is this different from `npx @hachej/boring-agent`?**
|
|
203
|
+
A: `@hachej/boring-ui-cli` ships the full workbench (file tree, editor, command palette, plugins). `@hachej/boring-agent` is just the agent + chat. The CLI is the batteries-included zero-config entry point.
|
|
204
|
+
|
|
205
|
+
**Q: Can I extend the CLI with plugins?**
|
|
206
|
+
A: Not directly in v1. The CLI uses the default agent + workspace configuration. For plugin extensibility, build a custom app using `@hachej/boring-workspace` + `@hachej/boring-core`.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Building Something Bigger?
|
|
211
|
+
|
|
212
|
+
`@hachej/boring-ui-cli` is the zero-config entry point. For a full app with:
|
|
213
|
+
- Multi-user authentication
|
|
214
|
+
- Persistent workspaces with Postgres
|
|
215
|
+
- Email invites and password resets
|
|
216
|
+
- Custom domain plugins
|
|
217
|
+
|
|
218
|
+
See the [boring-ui monorepo](https://github.com/hachej/boring-ui) and its packages:
|
|
219
|
+
|
|
220
|
+
| Package | Purpose |
|
|
221
|
+
|---------|---------|
|
|
222
|
+
| `@hachej/boring-core` | Auth, DB, app factory, multi-user |
|
|
223
|
+
| `@hachej/boring-workspace` | Plugin system, panels, layouts |
|
|
224
|
+
| `@hachej/boring-agent` | Agent runtime, tools, chat UI |
|
|
225
|
+
| `@hachej/boring-ui-kit` | Shared React UI primitives |
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
*About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
|
|
230
|
+
|
|
38
231
|
---
|
|
39
232
|
|
|
40
|
-
##
|
|
233
|
+
## License
|
|
41
234
|
|
|
42
|
-
|
|
235
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -1,142 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import fastifyStatic from "@fastify/static";
|
|
4
|
-
import { AuthStorage, LoginDialogComponent, OAuthSelectorComponent, initTheme } from "@mariozechner/pi-coding-agent";
|
|
5
|
-
import { ProcessTerminal, TUI } from "@mariozechner/pi-tui";
|
|
6
|
-
import { execSync } from "node:child_process";
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
8
|
-
import { createInterface } from "node:readline";
|
|
9
|
-
import { basename, dirname, resolve } from "node:path";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
10
3
|
import { fileURLToPath } from "node:url";
|
|
11
|
-
import {
|
|
12
|
-
const { values: args } = parseArgs({
|
|
13
|
-
options: {
|
|
14
|
-
port: { type: "string", short: "p" },
|
|
15
|
-
host: { type: "string" },
|
|
16
|
-
mode: { type: "string", short: "m" }
|
|
17
|
-
},
|
|
18
|
-
strict: false
|
|
19
|
-
});
|
|
20
|
-
const PORT = Number(args.port ?? process.env.PORT) || 5200;
|
|
21
|
-
const HOST = args.host ?? process.env.HOST ?? "0.0.0.0";
|
|
22
|
-
const MODE_MAP = {
|
|
23
|
-
"local": "direct",
|
|
24
|
-
// no sandbox, full network access
|
|
25
|
-
"local-sandbox": "local"
|
|
26
|
-
// bwrap isolated, no network (Linux only)
|
|
27
|
-
};
|
|
28
|
-
const rawMode = args.mode ?? process.env.BORING_MODE ?? "local-sandbox";
|
|
29
|
-
if (!(rawMode in MODE_MAP)) {
|
|
30
|
-
console.error(`
|
|
31
|
-
Error: invalid --mode "${rawMode}". Valid options: ${Object.keys(MODE_MAP).join(", ")}
|
|
32
|
-
`);
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
const CLI_MODE = rawMode;
|
|
36
|
-
const MODE = MODE_MAP[CLI_MODE];
|
|
4
|
+
import { runCli } from "./server/cli.js";
|
|
37
5
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
console.error("\nError: boring-ui frontend not found.");
|
|
43
|
-
console.error("Run `pnpm build:full` in packages/cli to build it first.\n");
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
function openBrowser(url) {
|
|
47
|
-
try {
|
|
48
|
-
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
49
|
-
execSync(`${opener} ${url}`, { stdio: "ignore" });
|
|
50
|
-
} catch {
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
async function loginWithPi(auth) {
|
|
54
|
-
const term = new ProcessTerminal();
|
|
55
|
-
const tui = new TUI(term, false);
|
|
56
|
-
initTheme("dark");
|
|
57
|
-
tui.start();
|
|
58
|
-
const providerId = await new Promise((resolve2, reject) => {
|
|
59
|
-
const selector = new OAuthSelectorComponent(
|
|
60
|
-
"login",
|
|
61
|
-
auth,
|
|
62
|
-
resolve2,
|
|
63
|
-
() => reject(new Error("Login cancelled"))
|
|
64
|
-
);
|
|
65
|
-
tui.addChild(selector);
|
|
66
|
-
tui.setFocus(selector);
|
|
67
|
-
tui.requestRender();
|
|
68
|
-
});
|
|
69
|
-
await new Promise((resolve2, reject) => {
|
|
70
|
-
const dialog = new LoginDialogComponent(tui, providerId, (success) => {
|
|
71
|
-
success !== false ? resolve2() : reject(new Error("Login cancelled"));
|
|
72
|
-
});
|
|
73
|
-
tui.addChild(dialog);
|
|
74
|
-
tui.setFocus(dialog);
|
|
75
|
-
tui.requestRender();
|
|
76
|
-
auth.login(providerId, {
|
|
77
|
-
onAuth: ({ url }) => {
|
|
78
|
-
openBrowser(url);
|
|
79
|
-
dialog.showAuth(url, "Your browser should open automatically. Waiting for login\u2026");
|
|
80
|
-
},
|
|
81
|
-
onProgress: (msg) => dialog.showProgress(msg),
|
|
82
|
-
onPrompt: ({ message }) => new Promise((res) => {
|
|
83
|
-
dialog.showManualInput(message);
|
|
84
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
85
|
-
rl.question("", (code) => {
|
|
86
|
-
rl.close();
|
|
87
|
-
res(code.trim());
|
|
88
|
-
});
|
|
89
|
-
})
|
|
90
|
-
}).catch(reject);
|
|
6
|
+
try {
|
|
7
|
+
await runCli({
|
|
8
|
+
argv: process.argv.slice(2),
|
|
9
|
+
publicDir: resolve(__dirname, "..", "public")
|
|
91
10
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
async function resolveApiKey() {
|
|
96
|
-
if (process.env.ANTHROPIC_API_KEY) return process.env.ANTHROPIC_API_KEY;
|
|
97
|
-
const auth = AuthStorage.create();
|
|
98
|
-
await auth.reload();
|
|
99
|
-
const stored = await auth.getApiKey("anthropic");
|
|
100
|
-
if (stored) return stored;
|
|
101
|
-
console.log("\nNo API key found \u2014 launching login\u2026\n");
|
|
102
|
-
await loginWithPi(auth);
|
|
103
|
-
const key = await auth.getApiKey("anthropic");
|
|
104
|
-
if (!key) {
|
|
105
|
-
console.error("\nLogin failed. Set ANTHROPIC_API_KEY manually.\n");
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
108
|
-
return key;
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
13
|
+
process.exit(1);
|
|
109
14
|
}
|
|
110
|
-
const apiKey = await resolveApiKey();
|
|
111
|
-
process.env.ANTHROPIC_API_KEY = apiKey;
|
|
112
|
-
console.log(`
|
|
113
|
-
${projectName}`);
|
|
114
|
-
console.log(` workspace ${workspaceRoot}`);
|
|
115
|
-
console.log(` mode ${CLI_MODE}`);
|
|
116
|
-
console.log(` port ${PORT}`);
|
|
117
|
-
console.log(` host ${HOST}`);
|
|
118
|
-
const app = await createWorkspaceAgentServer({
|
|
119
|
-
workspaceRoot,
|
|
120
|
-
mode: MODE,
|
|
121
|
-
logger: false
|
|
122
|
-
});
|
|
123
|
-
app.get("/api/v1/workspace/meta", async () => ({
|
|
124
|
-
workspaceRoot,
|
|
125
|
-
projectName
|
|
126
|
-
}));
|
|
127
|
-
await app.register(fastifyStatic, {
|
|
128
|
-
root: publicDir,
|
|
129
|
-
prefix: "/",
|
|
130
|
-
wildcard: false
|
|
131
|
-
});
|
|
132
|
-
app.setNotFoundHandler(async (req, reply) => {
|
|
133
|
-
if (req.url.startsWith("/api/")) {
|
|
134
|
-
return reply.code(404).send({ error: "Not found" });
|
|
135
|
-
}
|
|
136
|
-
return reply.sendFile("index.html", publicDir);
|
|
137
|
-
});
|
|
138
|
-
await app.listen({ port: PORT, host: HOST });
|
|
139
|
-
console.log(`
|
|
140
|
-
http://localhost:${PORT}
|
|
141
|
-
`);
|
|
142
|
-
openBrowser(`http://localhost:${PORT}`);
|